#ifndef _ARCH_MMU_H
#define _ARCH_MMU_H

#include <types.h>
#include <arch/arm.h>

/* Hardware page table definitions */

#define MMU_TYPE_MASK	(3 << 0)
#define MMU_TYPE_SECT	(2 << 0)
#define MMU_TYPE_TABLE	(1 << 0)
#define MMU_TYPE_SMALL	(2 << 0)

#define MMU_DOMAIN_MASK		(0xf << 5)
#define MMU_DOMAIN(x)		((x) << 5)
#define MMU_DOMAIN_KERNEL	0

#define MMU_DOMAIN_NOACCESS	0
#define MMU_DOMAIN_CLIENT	1
#define MMU_DOMAIN_MANAGER	3

#define MMU_CB_MASK			(3 << 2)
#define MMU_BUFFERABLE		(1 << 2)
#define MMU_CACHEABLE		(1 << 3)
#define MMU_UNCACHED		(0)
#define MMU_BUFFERED		(MMU_BUFFERABLE)
#define MMU_WRITETHROUGH	(MMU_CACHEABLE)
#define MMU_WRITEBACK		(MMU_CACHEABLE | MMU_BUFFERABLE)

#define MMU_L1_BIT4			(1 << 4)

#define MMU_SECT_AP_MASK	(3 << 10)
#define MMU_SECT_AP_READ	(0 << 10)
#define MMU_SECT_AP_WRITE	(1 << 10)

#define MMU_SMALL_AP_MASK	(0xff << 4)
#define MMU_SMALL_AP_READ	(0x00 << 4)
#define MMU_SMALL_AP_WRITE	(0x55 << 4)

/* page table definitions */

#define PGDI_SHIFT		20
#define PTRS_PER_PGD	4096

#define PTI_SHIFT		12
#define PTRS_PER_PT		256

#define PT_SIZE			(PTRS_PER_PT * sizeof(pte_t))
#define PGD_SIZE		(PTRS_PER_PGD * sizeof(pgde_t))

#define PTI_MASK		((1 << PGDI_SHIFT) - 1)
#define OFFSET_MASK		((1 << PTI_SHIFT) - 1)

typedef uint32_t pgde_t;
typedef uint32_t pgdf_t;
typedef uint32_t pte_t;

#define to_pgde(val)			((pgde_t)(val))
#define pgde_val(pgde)			((uint32_t)(pgde))
#define pgdi(addr)				((addr) >> PGDI_SHIFT)
#define get_pgde(addr, base)	((pgde_t *)base + pgdi(addr))
#define pgde_is_none(pgd)		(!pgde_val(pgd))
#define set_pgde(pgde, val)		(*((pgde_t *)(pgde)) = to_pgde(val))
#define clear_pgde(pgde)		(*((pgde_t *)(pgde)) = to_pgde(0))

#define to_pgdf(val)			((pgdf_t)(val))
#define pgdf_val(pgdf)			((uint32_t)(pgdf))
#define get_pgdf(addr, base)	((pgdf_t *)base + pgdi(addr))
#define pgdf_is_none(pgd)		(!pgdf_val(pgd))
#define set_pgdf(pgdf, val)		(*((pgdf_t *)(pgdf)) = to_pgdf(val))
#define clear_pgdf(pgdf)		(*((pgdf_t *)(pgdf)) = to_pgdf(0))

#define to_pte(val)			((pte_t)(val))
#define pte_val(pte)		((uint32_t)(pte))
#define pti(addr)			(((addr) >> PTI_SHIFT) & ((1 << (PGDI_SHIFT - PTI_SHIFT)) - 1))
#define get_pte(addr)		((pte_t *)(pgde_val(*get_pgde(addr)) & ~(PT_SIZE - 1)) + pti(addr))
#define pte_is_none(pte)	(!pte_val(pte))
#define set_pte(pte, val)	(*((pte_t *)(pte)) = to_pte(val))
#define pte_clear(pte)		(*((pte_t *)(pte)) = to_pte(0))

/* Hardware MMU definitions */

static inline void mmu_enable(void)
{
    set_cr(get_cr() | CR_M);  
}

static inline void mmu_disable(void)
{
    set_cr(get_cr() & ~CR_M); 
}

static inline int mmu_is_enabled(void)
{
	return get_cr() & CR_M;
}

static inline void tlbs_invalidate_all(void) 
{
    asm("mcr p15, 0, %0, c8, c7, 0"
        : 
        : "r" (0));
}

static inline void itlb_invalidate_all(void) 
{
    asm("mcr p15, 0, %0, c8, c5, 0"
        :
        : "r" (0));
}

static inline void dtlb_invalidate_all(void) 
{
    asm("mcr p15, 0, %0, c8, c6, 0"
        :
        : "r" (0));
}

#endif /* _ARCH_MMU_H */

